import Vue from 'vue'
import CryptoUtil from './CryptoUtil'
import EncryptlongUtil from './EncryptlongUtil'
import ReplaceKeys from './ReplaceKeys'

import _get from 'lodash/get'
import _set from 'lodash/set'

/*
** 加密说明
1.RSA加密/解密
1.1 客户端加密，服务端解密
私钥存储在服务端;
公钥通过接口返回，客户端通过该公钥进行RSA加密

2.RSA+AES加密/解密
2.1 客户端加密，服务端解密
#客户端生成32位的uuid做为key
#对uuid进行RSA加密
#通过请求头把RSA加密后的AESKey传给后端
#后端对AESKey进行RSA解密获取到AESKey，对业务数据进行解密

2.2 服务端加密，客户端解密
#服务端返回AES加密后的业务数据，及通过RSA加密后的AESKey
#客户端对AESKey进行RSA解密
#使用解密后的AESKey对业务数据进行解密
*/

export default class EncryptUtil {
  /**
   * 加解密工具
   * @param {router, axios, RSAPublicKeyConfig, reqestConfig, responseConfig, replaceKeys, websiteKey, isTest} options
   */
  init(options) {
    this.options = options
    this.tokenConfigKey = this.options.websiteKey + '-token-config'
    // 用于AES加密(加密/解密key不一样)
    this.cryptoUtil = new CryptoUtil()
    // 用于AES解密(加密/解密key不一样)
    this.cryptoUtil2 = new CryptoUtil()
    // RSA加解密
    this.encryptlongUtil = new EncryptlongUtil()
    this.replaceKeys = new ReplaceKeys(this.options.replaceKeys, this.options.isTest)

    this.cryptoUtil.init(this.options.isTest)
    this.cryptoUtil2.init(this.options.isTest)
    this.encryptlongUtil.init(this.options.isTest)
    this.loginUrl = ''
    // 配置信息
    this.tokenConfig = ''
    // 请求头名称
    this.AESKeyName = ''
    // 请求头值(rsa加密后的aesKEY)
    this.AESKeyEncryptValue = ''
    // rsa公钥
    this.RSAPublicKey = ''
    // rsa 私钥
    this.SSSK = ''

    this.bindAxios()
    this.bindRouter()
  }
  /**
   * 获取配置信息
   * @param {*} field 配置的字段，为空则获取全部
   * @returns
   */
  getTokenConfig(field) {
    if (this.tokenConfig) {
      return field ? this.tokenConfig[field] : this.tokenConfig
    }
    const tokenConfig = localStorage.getItem(this.tokenConfigKey)
    if (tokenConfig) {
      this.tokenConfig = JSON.parse(tokenConfig)
      return field ? this.tokenConfig[field] : ''
    }
    console.warn('配置信息不存在')
    return ''
  }
  /**
   * 获取登录url
   * @returns
   */
  getLoginUrl() {
    if (this.loginUrl) {
      return this.loginUrl
    }
    const loginUrl = this.options.reqestConfig.find(item => {
      return item.isLogin
    })
    this.loginUrl = loginUrl ? loginUrl.url : ''

    return this.loginUrl
  }
  /**
   * 设置AES加密密钥
   * @param {*} key
   */
  setAESKey = (key) => {
    // 设置rsa加密前的AESKey
    this.cryptoUtil.setKey(key)
    // 设置rsa加密后的AESKey
    const RSAPublicKey = this.getRSAPublicKey()
    if (!RSAPublicKey) {
      console.warn('RSAPublicKey 未设置')
      return
    }
    this.setRSAPublicKey(RSAPublicKey)

    this.AESKeyEncryptValue = this.encryptlongUtil.encrypt(key)
    this.cryptoUtil.setRSAEncryptKey(this.AESKeyEncryptValue)
  }
  /**
   * 获取rsa加密后的AESKey
   * @returns
   */
  getAESKeyEncrypt = () => {
    if (this.AESKeyEncryptValue) {
      return this.AESKeyEncryptValue
    }
    const aesKey = this.options.AESConfig.getAeskey()
    this.setAESKey(aesKey)
    return this.AESKeyEncryptValue
  }

  /**
   * 获取rsa私钥
   * @returns
   */
  getSSSKey = () => {
    if (this.SSSK) {
      return this.SSSK
    }
    this.SSSK = this.getTokenConfig('webPRK')
    return this.SSSK
  }
  /**
   * 获取解密后的aeskey
   * @param {*} eeekey
   * @returns
   */
  getEEEKeyBySSSKey(eeekey) {
    this.encryptlongUtil.setPrivateKey(this.getSSSKey())
    return this.encryptlongUtil.decrypt(eeekey)
  }
  /**
   * 获取请求头名称
   * @returns
   */
  getAESKeyName = () => {
    if (this.AESKeyName) {
      return this.AESKeyName
    }
    this.AESKeyName = this.getTokenConfig('aesKey')
    return this.AESKeyName
  }
  setRSAPublicKey(key) {
    this.encryptlongUtil.setPublicKey(key)
  }
  setRSAPrivateKey(key) {
    this.encryptlongUtil.setPrivateKey(key)
  }
  saveRSAPublicKey(data) {
    const RSAPublicKey = _get(data, this.options.RSAPublicKeyConfig.field)
    if (RSAPublicKey) {
      this.setRSAPublicKey(RSAPublicKey)
      localStorage.setItem(this.options.websiteKey + '-RSAPublicKey', RSAPublicKey)
    }
  }
  /**
   * 获取RSA加密公钥
   */
  getRSAPublicKey() {
    if (this.RSAPublicKey) {
      return this.RSAPublicKey
    }
    this.RSAPublicKey = this.getTokenConfig('apiPBK')
    return this.RSAPublicKey
  }

  requestPublicKey() {
    const { url, method } = this.options.RSAPublicKeyConfig
    this.options.axios({
      url, method
    }).then(res => {
      localStorage.setItem(this.options.websiteKey + '-token-config', JSON.stringify(res.data.data))
    })
  }

  setUriParam(config) {
    if (config.url.indexOf('?') !== -1) {
      const urlParams = config.url.substring(config.url.indexOf('?') + 1)
      const stringArr = urlParams.split('&')
      config.url = config.url.substring(0, config.url.indexOf('?'))
      for (var i = 0; i < stringArr.length; i++) {
        const kAv = stringArr[i].split('=')
        if (!config.params) {
          config.params = {}
        }
        config.params[kAv[0]] = kAv[1]
      }
    }
    return config
  }
  /**
 * 对请求数据进行加密
 * @param {*} config
 */
  encryptRequest(config) {
    // method url params
    const reqestCfg = this.options.reqestConfig.find(item => config.url === item.url)
    if (!reqestCfg || (config.method !== 'get' && config.method !== 'post')) {
      return config
    }
    if (this.options.isTest) {
      console.log('----------url----------')
      console.log(config.url)
    }
    let encryptEntity = null
    if (reqestCfg.type === 'RSA') {
      encryptEntity = this.encryptlongUtil
    } else {
      encryptEntity = this.cryptoUtil
    }
    const encryptField = config.params ? 'params' : 'data'
    config[encryptField] = this.encryptDataField(config[encryptField], reqestCfg, encryptEntity)

    return config
  }
  encryptValue(data, field, reqestCfg, encryptEntity) {
    const isFormData = data instanceof FormData

    const pathValue = isFormData ? data.get(field) : _get(data, field)
    let encryptValue = encryptEntity.encrypt(pathValue)
    if (reqestCfg.isUrlEncode) {
      encryptValue = encodeURIComponent(encryptValue)
    }
    if (isFormData) {
      data.set(field, encryptValue)
    } else {
      _set(data, field, encryptValue)
    }
  }
  encryptDataField(data, reqestCfg, encryptEntity) {
    const isFormData = data instanceof FormData

    if (reqestCfg.fields && reqestCfg.fields.length) {
    // 对每一个字段进行加密
      reqestCfg.fields.forEach(item => {
        this.encryptValue(data, item, reqestCfg, encryptEntity)
      })
    } else {
    // 全部加密
      if (isFormData) {
        for (const [key] of data) {
          this.encryptValue(data, key, reqestCfg, encryptEntity)
        }
        // for (const [key, value] of data) {
        //   console.log(key, value)
        // }
        return data
      }
      const tmpData = JSON.parse(JSON.stringify(data))
      data = {}
      let encryptData = encryptEntity.encrypt(tmpData)
      if (reqestCfg.isUrlEncode) {
        encryptData = encodeURIComponent(encryptData)
      }
      data['data'] = encryptData
    }

    return data
  }
  /**
   * 获取请求的url
   */
  getReqestUrl(configUrl) {
    let configUrlTmp = configUrl
    const index = configUrl.indexOf('?')
    if (index > -1) {
      configUrlTmp = configUrl.substring(0, index)
    }

    return configUrlTmp
  }
  /**
 * 对返回数据进行解密
 * @param {*} res
 */
  decryptResponse(res) {
    const configUrl = this.getReqestUrl(res.config.url)
    const resCfg = this.options.responseConfig.find(item => configUrl === item.url)
    if (resCfg) {
      const extra = res.data.extra
      // 对返回的aeskey进行rsa解密, 然后再解密数据
      if (!extra) {
        console.error('extra未返回，解密失败')
        return
      }
      const eees = this.getEEEKeyBySSSKey(extra)
      if (!eees) {
        console.error('extra解密失败')
        return
      }
      this.cryptoUtil2.setKey(eees)
      res.data.data = this.cryptoUtil2.decrypt(res.data.data)
    }
    return res
  }

  bindAxios() {
    this.options.axios.interceptors.request.use(config => {
      config = this.setUriParam(config)
      const name = this.getAESKeyName()
      if (name && this.getAESKeyEncrypt()) {
        config.headers[name] = this.getAESKeyEncrypt()
      }
      // 拦截用户信息，获取aes_key
      if (config.url === this.getLoginUrl()) {
        if (!this.encryptlongUtil.getPublicKey()) {
          // 服务器异常，请重试
          // eslint-disable-next-line no-undef
          Vue.prototype.$message.warning('服务器异常，请重试！')
          return
        }
      }
      // 加密
      return this.encryptRequest(this.replaceKeys.replaceKeyword(config))
    })

    this.options.axios.interceptors.response.use(res => {
      if (!res || !res.data) {
        return
      }
      const status = res.data.code || 200
      if (status === 200) {
        // 解密
        res = this.decryptResponse(res)
        // 拦截用户信息，获取aes_key
        if (res.config.url === this.getLoginUrl()) {
          if (res.config.data.get('grant_type') !== 'refresh_token') {
            // 非统一认证登录时
            const aesKey = this.options.AESConfig.getAeskey(res.data)
            this.setAESKey(aesKey)
          }
        }
      }
      return res
    })
  }
  bindRouter() {
    this.options.router.afterEach(to => {
      if (to.path === '/login') {
        this.requestPublicKey()
      }
    })
  }
}
